﻿/* Copyright © Hauer Engineering 2012 */


// ERD model
var ERDViewModel = function (options)
{
    var ctx = this;

    // ERD context
    ctx.options =
        {
            serviceUrl: null
        };

    if (options)
        $.extend(true, ctx.options, options);

    // Web Api service proxy
    ctx.apiServiceProxy = $.heng.apiServiceProxy(ctx.options.serviceUrl);

    // Object mapper
    ctx.mapFromJS = function (type, source, target, parent)
    {
        var mapping =
            {
                key: function (data)
                {
                    if (data instanceof type)
                        return ko.utils.unwrapObservable(data.attr.Id);
                    else
                        return ko.utils.unwrapObservable(data.Id);
                },

                create: function (options)
                {
                    return new type(parent, options.data);
                }
            };

        ko.mapping.fromJS(source, mapping, target);
    }

    // FK
    ctx.fk =
        {
            domains: ko.observableArray(),
            domainsLoadedAt: ko.observable(),
            loadDomains: function (successCallback)
            {
                ctx.apiServiceProxy.get($.heng.enum.api.Domain,
                    {
                        successCallback: function (domains)
                        {
                            ctx.mapFromJS(ERDDomain, domains, ctx.fk.domains, ctx);

                            ctx.fk.domainsLoadedAt(new Date());

                            if (successCallback)
                                successCallback();
                        }
                    });
            },

            entityRelations: ko.observableArray()
        };

    ctx.UI =
    {
        selected: ko.observable(),        
        setSelected: function (obj)
        {
            ctx.UI.selected(obj);
            ctx.UI.setSelectedAttributes(obj);
        },

        selectedAttributes: ko.observableArray(),
        setSelectedAttributes: function (obj)
        {
            ctx.UI.selectedAttributes.removeAll();

            if (!obj || typeof (obj) != 'object' || !obj.attr)
                return;

            for (var prop in obj.attr)
            {
                ctx.UI.selectedAttributes.push(
                    {
                        name: prop,
                        value: obj.attr[prop],
                        editMode: ko.observable(false),
                        enterEditMode: function (data)
                        {
                            data.editMode(true);
                        },
                        exitEditMode: function (data)
                        {
                            data.editMode(false);
                        },
                        onChange: function (data)
                        {
                            ctx.UI.selected().save();
                        }
                    });
            }
        },
    };

    ctx.UI.isSelected = ko.computed(function ()
    {
        if (ctx.UI.selected() && ctx.UI.selected().attr)
            return true;
        return false;
    });
};


// Associated to Domain entity
var ERDDomain = function (model, domain)
{
    var ctx = this;

    ctx.typeName = 'Domain';

    // parent
    ctx.model = model;

    // attributes
    ctx.attr = ko.mapping.fromJS(domain, {}, ctx);

    // FK
    ctx.fk =
        {
            // Entities            
            entities: ko.observableArray(),
            entitiesLoadedAt: ko.observable(),            
            loadEntities: function (successCallback)
            {
                ctx.model.apiServiceProxy.get($.heng.enum.api.DomainEntity,
                    {
                        data: { domainId: ctx.attr.Id() },
                        successCallback: function (entities)
                        {
                            model.mapFromJS(ERDEntity, entities, ctx.fk.entities, ctx);

                            ctx.fk.entitiesLoadedAt(new Date());

                            if (successCallback)
                                successCallback();
                        }
                    });
            }
        };

    // UI
    ctx.UI =
        {
            select: function ()
            {
                ctx.model.UI.setSelected(ctx);
            },

            graphVisible: ko.observable(false),
            toggleGraphVisible: function ()
            {
                var visible = !ctx.UI.graphVisible();
                ctx.UI.setGraphVisible(visible);
            },
            setGraphVisible: function(visible)
            {
                ctx.UI.graphVisible(visible);

                if (visible)
                {
                    // load entities
                    ctx.fk.loadEntities(function ()
                    {
                        // for each entity display it on graph
                        $(ctx.fk.entities()).each(function ()
                        {
                            this.UI.setGraphVisible(true);
                        });
                    });
                }
                else
                {
                    // for each entity display it on graph
                    $(ctx.fk.entities()).each(function ()
                    {
                        this.UI.setGraphVisible(false);
                    });
                }
            },

            detailsVisible: ko.observable(false),
            toggleDetailsVisible: function ()
            {
                var visible = !ctx.UI.detailsVisible();
                ctx.UI.setDetailsVisible(visible);
            },
            setDetailsVisible: function(visible)
            {
                ctx.UI.select();
                if (visible)
                {
                    ctx.fk.loadEntities(function ()
                    {
                        ctx.UI.detailsVisible(true);
                    });
                }
                else
                {
                    ctx.UI.detailsVisible(false);
                }
            },

            name: ko.computed(function ()
            {
                return ctx.attr.Name() + (ctx.fk.entitiesLoadedAt() ? '   (' + ctx.fk.entities().length + ' entities)' : '');
            })
        };

    ctx.save = function ()
    {
        /*var json = ko.toJSON(ctx.attr);

        ctx.model.apiServiceProxy.put($.heng.enum.api.Domain,
            {
                json: json,
                successCallback: function (domain)
                {
                    alert('success');
                }
            });*/
    }
};


// Associated to DomainEntity entity
var ERDEntity = function (domain, entity)
{
    var ctx = this;

    ctx.typeName = 'Entity';

    // parent
    ctx.domain = domain;

    // attributes
    ctx.attr = ko.mapping.fromJS(entity, {}, ctx);

    // FK
    ctx.fk =
        {
            relationsLoadedAt: ko.observable(),
            // relations are retrieved from relations array which is a member of view model and filtering by current entity ID
            relations: ko.computed(function ()
            {
                var entRelations = $.grep(ctx.domain.model.fk.entityRelations(), function (rel, index)
                {
                    return rel.attr.DomainEntityAId() == ctx.attr.Id() || rel.attr.DomainEntityBId() == ctx.attr.Id();
                });

                return entRelations;
            }),
            loadRelations: function (successCallback)
            {
                ctx.domain.model.apiServiceProxy.get($.heng.enum.api.DomainEntityRelation,
                    {
                        data: { entityId: ctx.attr.Id() },
                        successCallback: function (relations)
                        {
                            ctx.domain.model.mapFromJS(ERDEntityRelation, relations, ctx.domain.model.fk.entityRelations, ctx);

                            ctx.fk.relationsLoadedAt(new Date());

                            if (successCallback)
                                successCallback();
                        }
                    });
            },

            // Attributes
            attributesLoadedAt: ko.observable(),
            attributes: ko.observableArray(),
            loadAttributes: function (successCallback)
            {
                ctx.domain.model.apiServiceProxy.get($.heng.enum.api.DomainEntityAttribute,
                    {
                        data: { entityId: ctx.attr.Id() },
                        successCallback: function (attributes)
                        {
                            ctx.domain.model.mapFromJS(ERDEntityAttribute, attributes, ctx.fk.attributes, ctx);

                            ctx.fk.attributesLoadedAt(new Date());

                            if (successCallback)
                                successCallback();
                        }
                    });
            }
        };

    // UI
    ctx.UI =
        {
            select: function ()
            {
                ctx.domain.model.UI.setSelected(ctx);
            },

            graphVisible: ko.observable(false),
            toggleGraphVisible: function ()
            {
                var visible = !ctx.UI.graphVisible();
                ctx.UI.setGraphVisible(visible);
            },
            setGraphVisible: function (visible)
            {
                if (visible)
                {
                    ctx.domain.UI.graphVisible(true);
                }

                if (!visible)
                {
                    ERDEntity.events.raise_removingFromGraph(ctx.domain.model,
                        {
                            entity: ctx
                        });
                }

                if (visible)
                {
                    // load attributes
                    ctx.fk.loadAttributes(function ()
                    {
                        // load relations
                        ctx.fk.loadRelations(function ()
                        {
                            // add entity to graph
                            ctx.UI.graphVisible(true);

                            // notify others that a new entity was added to graph
                            ERDEntity.events.raise_addedToGraph(ctx.domain.model,
                                    {
                                        entity: ctx
                                    });
                        });
                    });
                }
                else
                {
                    ctx.UI.graphVisible(false);
                }
            },

            detailsVisible: ko.observable(false),
            toggleDetailsVisible: function ()
            {
                ctx.UI.select();

                var visible = !ctx.UI.detailsVisible();                
                ctx.UI.setDetailsVisible(visible);
            },
            setDetailsVisible: function(visible)
            {
                ctx.UI.detailsVisible(visible);
            },            

            attributesVisible: ko.observable(false),
            toggleAttributesVisible: function()
            {
                var visible = !ctx.UI.attributesVisible();
                ctx.UI.setAttributesVisible(visible);
            },
            setAttributesVisible: function(visible)
            {
                if (visible)
                {
                    ctx.fk.loadAttributes(function ()
                    {
                        ctx.UI.attributesVisible(true);
                    });
                }
                else
                {
                    ctx.UI.attributesVisible(false);
                }
            },

            relationsVisible: ko.observable(false),
            toggleRelationsVisible: function()
            {
                var visible = !ctx.UI.relationsVisible();
                ctx.UI.setRelationsVisible(visible);
            },
            setRelationsVisible: function (visible)
            {
                if (visible)
                {
                    ctx.fk.loadRelations(function ()
                    {
                        ctx.UI.relationsVisible(true);
                    });
                }
                else
                {
                    ctx.UI.relationsVisible(false);
                }
            },

            // class for entity element
            entityElId: ko.computed(function ()
            {
                return 'erd-entity-id-' + ctx.attr.Id().toString();
            }),

            // graph entity element corresponding to current entity
            entityEl: function ()
            {
                var entityIdClass = ctx.UI.entityElId();
                return $('#' + entityIdClass);
            },

            elementCreated: ko.observable(false)
        };

    ctx.save = function ()
    {
        alert('saving');
    };
};
ERDEntity.events =
    {
        add_removingFromGraph: function (model, handler)
        {
            $(model).bind('removingFromGraph.ERDEntity', handler);
        },
        raise_removingFromGraph: function (model, entity)
        {
            $(model).trigger('removingFromGraph.ERDEntity', entity)
        },

        add_addedToGraph: function (model, handler)
        {
            $(model).bind('addedToGraph.ERDEntity', handler);
        },
        raise_addedToGraph: function (model, entity)
        {
            $(model).trigger('addedToGraph.ERDEntity', entity);
        }
    };


// Associated to DomainEntityAttribute entity
var ERDEntityAttribute = function (entity, attr)
{
    var ctx = this;

    ctx.typeName = 'Attribute';

    // parent
    ctx.entity = entity;

    // attributes
    ctx.attr = ko.mapping.fromJS(attr, {}, ctx);
};
ERDEntityAttribute.prototype =
    {
        options: function ()
        {
            return this.entity.options();
        }
    };


// Associated to DomainEntityRelation entity
var ERDEntityRelation = function (entity, relation)
{
    var ctx = this;

    ctx.typeName = 'Relation';

    // parent
    ctx.entity = entity;

    // attributes
    ctx.attr = ko.mapping.fromJS(relation, {}, ctx);
};
ERDEntityRelation.prototype = 
    {
        options: function()
        {
            return this.entity.options();
        }
    };